package cn.javaxc.mvcwork.framework.mybatis;

import java.lang.reflect.Field;
import java.sql.*;
import java.util.List;
import java.util.*;


import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;

import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.ParameterMode;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.executor.statement.BaseStatementHandler;
import org.apache.ibatis.executor.statement.RoutingStatementHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.reflection.DefaultReflectorFactory;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.ReflectorFactory;
import org.apache.ibatis.reflection.factory.DefaultObjectFactory;
import org.apache.ibatis.reflection.factory.ObjectFactory;
import org.apache.ibatis.reflection.wrapper.DefaultObjectWrapperFactory;
import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;

import cn.javaxc.mvcwork.controller.MVCConfig;
import cn.javaxc.mvcwork.util.helper.ReflectHelper;
import cn.javaxc.mvcwork.controller.web.PagedListInfo;

@Intercepts({@Signature(type=StatementHandler.class,method="prepare",args={Connection.class,Integer.class})})
public class PagedPlugin implements Interceptor {

	protected static final ObjectFactory DEFAULT_OBJECT_FACTORY = new DefaultObjectFactory();
	protected static final ObjectWrapperFactory DEFAULT_OBJECT_WRAPPER_FACTORY = new DefaultObjectWrapperFactory();
	protected static final ReflectorFactory DEFAULT_REFLECTOR_FACTORY= new DefaultReflectorFactory();
	
	public Object intercept(Invocation ivk) throws Throwable {

		// TODO Auto-generated method stub
		if(ivk.getTarget() instanceof StatementHandler){
			StatementHandler statementHandler = (StatementHandler)ivk.getTarget();
			MetaObject metaStatementHandler = MetaObject.forObject(statementHandler,  
		     DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY,DEFAULT_REFLECTOR_FACTORY); 
			ParameterHandler parameterHandler = statementHandler.getParameterHandler();
			BoundSql bsql = statementHandler.getBoundSql();
			BaseStatementHandler delegate = (BaseStatementHandler) ReflectHelper.getValueByFieldName(statementHandler, "delegate");
			MappedStatement mappedStatement = (MappedStatement) ReflectHelper.getValueByFieldName(delegate, "mappedStatement");
			RowBounds rowBounds = (RowBounds)ReflectHelper.getValueByFieldName(delegate, "rowBounds");
			String sqlid=mappedStatement.getId();
			Connection db = (Connection) ivk.getArgs()[0];

			if (rowBounds instanceof PagedListInfo) {
				PagedListInfo pagedListInfo=(PagedListInfo)rowBounds;
				pagedListInfo.setMaxRecords(countBatis(db,parameterHandler.getParameterObject(),bsql,sqlid,mappedStatement.getConfiguration()));
//				if ("MYSQL".equals(mappedStatement.getConfiguration().getProperty("dialect").toUpperCase())){
//				
//				}
			   metaStatementHandler.setValue("delegate.boundSql.sql", bsql.getSql()+"\nlimit "+pagedListInfo.getCurrentOffset()+","+pagedListInfo.getItemsPerPage() );
				pagedListInfo.setMode(pagedListInfo.LIST_ALL);
			}
		}
		return ivk.proceed();
	}


	
	public Object plugin(Object arg) {
		// TODO Auto-generated method stub
		return Plugin.wrap(arg, this);
	}	
	public void setProperties(Properties p) {

	}

	public int countBatis(Connection db,Object reqParasMap,BoundSql bsql,String sqlid,Configuration config) throws SQLException{
	  int count = 0;

		List reqParasList=new ArrayList();
		if (config.hasStatement(sqlid+"_count")){
			bsql=config.getMappedStatement(sqlid+"_count").getBoundSql(reqParasMap);
		}
		String countSql=bsql.getSql();
		if (countSql.trim().isEmpty())return count;
		
		for (ParameterMapping para:bsql.getParameterMappings() ){
			if (bsql.hasAdditionalParameter(para.getProperty())){
				reqParasList.add(bsql.getAdditionalParameter(para.getProperty()));
			}else if(reqParasMap instanceof Map){
				reqParasList.add(((Map)reqParasMap).get(para.getProperty()));					
			}
		}
		if (!config.hasStatement(sqlid+"_count")){		
			countSql = "select count(*) from (" + countSql+ ") tmp_count";
		}
		return countBatis(db,sqlid,countSql,reqParasList);
	
	}
	public int countBatis(Connection db,String sqlid,String countSql,List reqParasList) throws SQLException{
		int count = 0;
		PreparedStatement countStmt = db.prepareStatement(countSql);
		for (int i=0;i<reqParasList.size() ;i++ ){
			countStmt.setObject(i+1,reqParasList.get(i));
		}
		ResultSet rs = countStmt.executeQuery();
		if (rs.next()) {
			count = rs.getInt(1);
		}
		rs.close();
		countStmt.close();
	  return count;	
	}
}
